#ifndef _WAVE_SIMULATOR_H_
#define _WAVE_SIMULATOR_H_

#ifndef L_VERSION
#define L_VERSION   1120130
#endif

#include <llib.h>
using namespace llib;
using namespace llib::math;
using namespace llib::geom;

#include <vector>
using namespace std;

class WaveSimulator2 : public Simulator {
private:
    Int32       _width;
    Int32       _height;
    Float       _spacing;

    Int32       _stepId;
    Boolean     _pause;

    SceneObj<BVTree4>    _surface;

    Vec3f       _center;
    const Float _centerZspeed;
    const Float _centerZmax;
    const Float _centerZmin;
    const Float _extentArg;
    const Float _waveSpeed;

    const Vec3f _origin;


    Vec3f curl(const Vec3f& position)
    {
        if(abs(_center.y) < EPS_F) return position;
        Vec3f res;
        Float arcLen = Vec2f(position.x,position.z).Length();
        Float height = _center.y;
        Float alpha = arcLen / height;
        res.y = height * (1 - cos(alpha));
        Float radio = height * sin(alpha);
        Float theta = radio / arcLen;
        res.x = position.x * theta;
        res.z = position.z * theta;
        return res;
    }

    void updateVertex()
    {
        Int32 wVerNum = _width + 1;
        Int32 hVerNum = _height + 1;
        Vec3f tmpP;
        Float extentLen;
        Vec3f dir;
        TriMesh& mesh = _surface.GetTriMesh();
        FOR(h,0,hVerNum) {
            FOR(w,0,wVerNum) {
                Int32 index = h * wVerNum + w;
                tmpP.x = Float((w - wVerNum / 2.0) * _spacing);
                tmpP.z = Float((h - hVerNum / 2.0) * _spacing);
                tmpP.y = 0.f;
                extentLen = Float(_extentArg * sin(tmpP.x*8.0+_waveSpeed*_stepId) * cos(tmpP.z*8.0+_waveSpeed*_stepId));
                tmpP = curl(tmpP);
                dir = _center - tmpP;
                dir.Normalize();
                tmpP += dir * extentLen;
                mesh.GetVertex(index).GetRef() = tmpP;
            }
        }

    }

    Vec3f getNormal(MeshFace& face)
    {
        Vec3f v1 = face.GetVertex(1).GetRef() - face.GetVertex(0).GetRef();
        Vec3f v2 = face.GetVertex(2).GetRef() - face.GetVertex(0).GetRef();
        return v1.Cross(v2);
    }

    Float speedAlter(Float input)
    {
        return Float(input * input / (_centerZmax - _centerZmin));
    }

public:
    WaveSimulator2() :
      _center       ( Vec3f(0.f,10.f,0.f) ),
      _origin       ( Vec3f(0.f,0.f,0.f) ),
      _width        ( 40 ),
      _height       ( 40 ),
      _spacing      ( 0.04f ),
      _stepId       ( 0 ),
      _pause        ( FALSE ),
      _centerZspeed ( 0.01f ),
      _centerZmax   ( 5.f ),
      _centerZmin   ( 0.2f ),
      _extentArg    ( 0.1f ),
      _waveSpeed    ( 0.02f )
    {
        // construct vertex and triangle
        Int32 wVerNum = _width + 1;
        Int32 hVerNum = _height + 1;
        Int32 verNum = wVerNum * hVerNum;
        vector<Vec3f> vertexList(verNum);
        FOR(h,0,hVerNum) {
            FOR(w,0,wVerNum) {
                Int32 index = h * wVerNum + w;
                vertexList[index].x = Float((w - wVerNum / 2.0) * _spacing);
                vertexList[index].z = Float((h - hVerNum / 2.0) * _spacing);
                vertexList[index].y = 0.f;
            }
        }
        const static Int32 triMap[2][2][3] = { { {0, 3, 1}, {1, 3, 2} }, { {0, 3, 2}, {0, 2, 1} } };
        Int32 triNum = _width * _height * 2;
        vector<Vec3i> triangleList(triNum);
        Int32 quadIndex[4];
        FOR(h,0,_height) {
            FOR(w,0,_width) {
                Int32 index = (h * _width + w) * 2;
                Int32 type = (w + h) % 2 == 0 ? 0 : 1;
                quadIndex[0] = h * wVerNum + w;
                quadIndex[1] = quadIndex[0] + 1;
                quadIndex[2] = quadIndex[1] + wVerNum;
                quadIndex[3] = quadIndex[0] + wVerNum;
                FOR(tri,0,2) {
                    FOR(axis,0,3) {
                        triangleList[index+tri][axis] = quadIndex[ triMap[type][tri][axis] ];
                    }
                }
            }
        }

        _surface.Init(vertexList, triangleList);

    }

    virtual Boolean Render()
    {
        //cout << _stepId << endl;

        if(!_pause) {

        /* update center */
        Float centerMoveDis = _centerZspeed * _stepId;
        Float range = (_centerZmax - _centerZmin) * 2;  // for up and down
        Int32 cycle = Int32(centerMoveDis / range);
        centerMoveDis -= range * cycle;
        if(centerMoveDis < _centerZmax - _centerZmin) {
            _center.y = _centerZmin + speedAlter((_centerZmax - _centerZmin) - centerMoveDis);
        } else {
            _center.y = _centerZmin + speedAlter(centerMoveDis - (_centerZmax - _centerZmin));
        }

        updateVertex();

        /* update tree first */
        _surface.UpdateTree();

        /* self collision check */
        _surface.SelfCollisonDetect();

        /* process collision response */
        _surface.CollisionResponse();

        }

        /* model drawing */
        const static Vec3f colorCollisionFree = Vec3f(0.5f,0.5f,0.5f);
        const static Int32 colorSize = 6;
        const static Vec3f colorCollision[colorSize] = 
            { Vec3f(1.f,0.2f,0.2f), Vec3f(0.2f,1.f,0.2f), Vec3f(0.2f,0.2f,1.f),
              Vec3f(0.7f,0.7f,0.2f),Vec3f(0.7f,0.2f,0.7f),Vec3f(0.2f,0.7f,0.7f) };
        const static Vec3f lineColor = Vec3f(0.3f,0.3f,0.3f);

        TriMesh& meshModel = _surface.GetTriMesh();
        vector<Int32>& collisionFlag = _surface.GetCollision();
        FOR(f,0,meshModel.GetFaceNum()) {
            MeshFace& face = meshModel.GetFace(f);
            if(collisionFlag[f] != 0) {
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colorCollision[collisionFlag[f]%colorSize].cell);
                glColor3fv(colorCollision[collisionFlag[f]%colorSize].cell);

                Vec3f& normal = getNormal(face);
                glNormal3fv(normal.cell);
                glBegin(GL_TRIANGLES);
                    glVertex3fv(face.GetVertex(0).GetRef().cell);
                    glVertex3fv(face.GetVertex(1).GetRef().cell);
                    glVertex3fv(face.GetVertex(2).GetRef().cell);
                glEnd();
            }

            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, lineColor.cell);
            glColor3fv(lineColor.cell);
            glBegin(GL_LINES);
                glVertex3fv(face.GetVertex(0).GetRef().cell);
                glVertex3fv(face.GetVertex(1).GetRef().cell);
                glVertex3fv(face.GetVertex(1).GetRef().cell);
                glVertex3fv(face.GetVertex(2).GetRef().cell);
                glVertex3fv(face.GetVertex(2).GetRef().cell);
                glVertex3fv(face.GetVertex(0).GetRef().cell);
            glEnd();
        }


        if(!_pause) ++_stepId;

        return TRUE;
    }

    virtual Boolean OnKeyDown(Uint8 key, Int32 x, Int32 y, Uint8 modifiers)
    {
        if(key == 32) { /*space*/
            _pause = !_pause;
        }
        return TRUE;
    }

};

#endif
